iT邦幫忙

2022 iThome 鐵人賽

0
自我挑戰組

菜雞也能優雅的征服RxJS系列 第 32

菜雞們一起征服RxJS -day32: 進一步驗證startWith 位置擺放的重要性

  • 分享至 

  • xImage
  •  

  • 我們在昨天day31有介紹到startWithendWith的用法,也建議大家將startWith放置於pipe底下,避免不可預期的情形發生。

  • 今天我們來寫一個範例,並進一步驗證startWith擺在不同地方會發生什麼情形!

case: 模擬一個start & stop的計時器,並顯示訊息

  • 我們在上個例子說明了startWithendWith擺放順序的重要性,我們來寫個例子

需求:

  1. 一個起始按鈕: start
  2. 一個停止按鈕: stop
  3. 一個顯示數字倒數
  • 這裡我們定義一個變數countDownMaxNum,來提供scan的初始值及startWith使用。
  • 可參考day12-scan複習如何寫一個倒數計時器。
  1. 一個狀態顯示: start(綠色)/stop(紅色)

stackblitz

  • html
...
<body>
  <button id="start">start</button>
  <button id="stop">stop</button>
  <h1 id="number">-</h1>
 </body>
 ...
  • javascript
import {
  endWith,
  from,
  fromEvent,
  interval,
  map,
  startWith,
  scan,
  takeUntil,
  takeWhile,
  switchMap,
  tap,
  throwError,
} from 'rxjs';

//elems
const start = document.getElementById('start');
const stop = document.getElementById('stop');
const num = document.getElementById('number');

// RxJS
const start$ = fromEvent(start, 'click');
const stop$ = fromEvent(stop, 'click');
const timer$ = interval(1000);

// === Step1: 建置一個倒數計時器,並宣告一個變數countDownMaxNum提供給scan及startWith使用 ===
const countDownMaxNum = 10;
const countDown$ = timer$.pipe(
  map(() => -1),
  scan((accu, current) => accu + current, countDownMaxNum),
  takeWhile((d) => d > 0),
  takeUntil(stop$)
);

// === Step2: 設定click to start,並觀察不同位置的startWith有什麼不同 ===
console.log('=== startWith放在pipe的底部 ===');
const clickStart$ = start$
  .pipe(
    // 觀察看看startWith放位置(1)及(2)有什麼不同
    // startWith(countDownMaxNum), //<-- (1)
    switchMap(() => countDown$),
    startWith(countDownMaxNum) //<--(2) 現在測試這個位置
  )
  .subscribe((d) => (num.innerHTML = `${d}`));

解析1: 先來看看startWith放在(2)的位置

  1. 使用者尚未開始點擊時,startWith就先拋出初始值countDownMaxNum,提供顯示在num.innerHTML之中,所以從頁面可以看到初始值是10
  2. 使用者點擊start,開始倒數,中途按下stop,數字靜止。
  3. 此時,如果使用者接續按start,數字從9開始,而不是我們預期的10??
  • 原因是,

startWithobservable開始之前,會優先發送一個數值給observer.next(),因此我們可以如期在頁面初始狀態上,看到10這個數字。
在那之後,startWith就不會再發送,也因此,當你再度按下start,數字會直接從9開始,也就是直接跑scan送出的值。

解析2: 將startWith放在(1)的位置看看發生什麼事

...
console.log('=== startWith放在pipe的 "最上層" ===');
const clickStart$ = start$
  .pipe(
    // 觀察看看startWith放位置(1)及(2)有什麼不同
    startWith(countDownMaxNum), //<-- (1)  現在測試這個位置
    switchMap(() => countDown$)
    // startWith(countDownMaxNum) //<--(2)
  )
  .subscribe((d) => (num.innerHTML = `${d}`));

顯示結果: 等等!我都還沒點擊就偷跑計數了!!??

  • 原因是,startWith擺在switchMap之前,startWith在點擊之前發出訊號10間接觸發了switchMap開始計數,才會還沒點擊就先偷跑計數啦!

結果整理

✍Recap

  • startWith擺放的位置,確實會影響,因為他會優先於observable訂閱運作之前,先行發出訊號,如果後方有其他的operator,自然會將數值傳遞過去,提前運作,造成不可預期的現象。

  • 接下來我會不定期的持續發佈RxJS的學習心得,也請大家繼續支持! /images/emoticon/emoticon41.gif


上一篇
菜雞們一起征服RxJS - day31: 在observable執行"之前(startWith)"以及"最後(endWith)"幫我放入資料
系列文
菜雞也能優雅的征服RxJS32
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言